
// create MuseX object table
MuseX = {};

// vgui object information
MuseX.VGUI = nil;
MuseX.Splash = 0;

// setup the media table
MuseX.Player = MXPlayer.New();

// CurTime used by MuseX
MXCurTime = UnPredictedCurTime;


/*------------------------------------
	initialize addon
------------------------------------*/
function MuseX.Init( )

	// check if data folder exists
	if ( !file.Exists( "musex/" ) ) then
	
		// if not, create it
		file.CreateDir( "musex" );
		
	end
	
	// check if playlists folder exists
	if ( !file.Exists( "musex/playlists" ) ) then
	
		// if not, create it
		file.CreateDir( "musex/playlists" );
		
	end
	
	// initialize settings
	MuseX.InitSettings();
	
	// load language file
	MuseX.LoadLanguage();
	
	// load the god damn skins
	MuseX.LoadSkins();
	
	// load the fucking visualizations
	// P.S. I'm so sick and tired of writing the word "visualizations"
	MuseX.LoadVisualizations();
	
	// initialize hotkeys
	MuseX.InitHotKeys();

end


/*------------------------------------
	main addon think routine
------------------------------------*/
local function MuseXThink( )
	
	// call the visualization think routine
	MuseX.VisualizationThink();
	
	// call the hot keys think routine
	MuseX.HotKeysThink();
	
	// check if a song is playing or paused
	if ( MuseX.IsPlaying() || MuseX.IsPaused() ) then
	
		// get the total length
		local duration = MuseX.Player:GetDuration();
		
		// get the current position
		local pos = MuseX.Player:GetCurrentPosition();
		
		// calculate the fraction
		local frac = pos / duration;
		
		// do clamping
		frac = math.Clamp( frac, 0, 1 );
		
		// validate the vgui
		if ( MuseX.VGUIValid() ) then
		
			// move the slider to match position
			MuseX.SetSeekPosition( frac );
			
		end
		
		// check if the song is done
		if ( frac == 1 ) then
			
			// move to next song (check if it succeeded)
			if ( !MuseX.NextSong() ) then
			
				// stop the song
				MuseX.Stop();
				
			end
		
		end
		
	end

end
hook.Add( "Think", "MuseX Think", MuseXThink );


/*------------------------------------
	main console command
------------------------------------*/
concommand.Add( "musex", function( pl, cmd, args )

	// toggle boggle woggle wiggle woozle
	MuseX.ToggleVGUI();
	
end );


/*------------------------------------
	load main vgui object
------------------------------------*/
function MuseX.LoadVGUI( )

	// function globals
	local width, height, x, y;

	// get the current view mode
	local viewmode = MuseX.GetSetting( "viewmode", MXSETTING_NUM );
	
	// merge the skin
	MuseX.MergeSkin();
	
	// check if the vgui object has been loaded and is valid
	if ( !MuseX.VGUIValid() ) then
	
		// clear the hook list
		MuseX.HookList = {};
		
		// determine which vgui to load
		local vguitype = ( viewmode == 2 ) && "MXCompact" || "MXMain";
	
		// create it
		MuseX.VGUI = vgui.Create( vguitype );
		
		// get width and height
		width, height = MuseX.VGUI:GetSize();
		
		local window = ( viewmode == 2 ) && "compact" || "main";
		
		// start position
		x = MuseX.GetSetting( ("%swindowx"):format( window ), MXSETTING_NUM ) || ( ( ScrW() * 0.5 ) - ( width * 0.5 ) );
		y = MuseX.GetSetting( ("%swindowy"):format( window ), MXSETTING_NUM ) || ( ( ScrH() * 0.5 ) - ( height * 0.5 ) );
		
		// not visible
		MuseX.VGUI:SetVisible( false );
		
		// set size and position
		MuseX.VGUI:SetSize( width, height );
		
		// set position
		MuseX.VGUI:SetPos( x, y );
		
		// load "nowplaying" playlist
		MuseX.LoadPlaylist( "nowplaying" );
	
	end
	
	// apply the skin
	MuseX.ApplySkin();
		
	// check if the splash screen has been displayed
	if ( MuseX.Splash == 0 ) then
	
		// update splash status
		MuseX.Splash = 1;
		
		// splash screen size
		local w = 250;
		local h = 250;
		
		// create splash screen
		local splash = vgui.Create( "MXSplash" );
		
		// size & position the splash screen
		splash:SetSize( w, h );
		splash:SetPos( ( ScrW() * 0.5 ) - ( w * 0.5 ), ( ScrH() * 0.5 ) - ( h * 0.5 ) );
		
		// not visible
		splash:SetVisible( false );
		
		// open it
		splash:Open();
		
	// check if the splash screen is currently opening
	elseif ( MuseX.Splash == 1 ) then
	
		// bai
		return;
		
	// splash is all done, just open it
	else
	
		// go go gadget MuseX
		MuseX.VGUI:Open();
	
	end

end


/*------------------------------------
	toggle the vgui
------------------------------------*/
function MuseX.ToggleVGUI( )

	// check if the vgui is loaded
	if ( MuseX.VGUIValid() && MuseX.Splash == 2 ) then
	
		// check if it is hidden
		if ( MuseX.VGUI.IsHiding ) then
		
			// open it
			MuseX.VGUI:Open();
			
		// must be visible
		else
		
			// hide it
			MuseX.VGUI:Hide();
		
		end
		
	else
	
		// load the vgui
		MuseX.LoadVGUI();
	
	end
			
end


/*------------------------------------
	shutdown MuseX
------------------------------------*/
function MuseX.Close( instant, keepalive )

	// check if we're killing the song too
	if ( !keepalive ) then
	
		// stop
		MuseX.Stop();
	
	end

	// validate the vgui
	if ( MuseX.VGUIValid() ) then
	
		// get delay time
		local delay = ( instant ) && 0 || 0.3;
	
		// delay the vgui kill (fixes a problem with sticking tooltips)
		timer.Simple( delay, function() 
		
			// check the vgui, again, just in case
			if ( MuseX.VGUIValid() ) then
			
				// kill the vgui
				MuseX.VGUI:Remove();
				
			end
			
		end );
	
	end
	
	// flag the splash appropriately
	MuseX.Splash = ( keepalive ) && 2 || 0;

end


/*------------------------------------
	restart MuseX
------------------------------------*/
function MuseX.Restart( keepalive )

	// check if the vgui is valid
	if ( MuseX.VGUIValid() ) then
	
		// hide it
		MuseX.VGUI:Hide();
		
	end
	
	// delay the shutdown
	timer.Simple( 0.2, function()
	
		// kill it
		MuseX.Close( true, keepalive );
		
		// delay the restart
		timer.Simple( 0.1, function() MuseX.LoadVGUI(); MuseX.UpdateSongInfo(); end );
		
	end );
		
end


/*------------------------------------
	add songs to playlist
------------------------------------*/
function MuseX.AddToPlaylist( songs )

	// validate the vgui
	if ( !MuseX.VGUIValid() ) then
	
		// not valid
		return;
	
	end
	
	// pass to vgui object
	MuseX.VGUI:AddToPlaylist( songs );

end


/*------------------------------------
	remove songs from playlist
------------------------------------*/
function MuseX.RemoveFromPlaylist( songs )

	// validate the vgui
	if ( !MuseX.VGUIValid() ) then
	
		// not valid
		return;
	
	end
	
	// pass to vgui object
	MuseX.VGUI:RemoveFromPlaylist( songs );

end


/*------------------------------------
	remove all songs from playlist
------------------------------------*/
function MuseX.ClearPlaylist( )

	// validate the vgui
	if ( !MuseX.VGUIValid() ) then
	
		// not valid
		return;
	
	end
	
	// pass to vgui object
	MuseX.VGUI:ClearPlaylist();
	
end


/*------------------------------------
	get all songs in playlist
------------------------------------*/
function MuseX.GetPlaylist( )

	// validate the vgui
	if ( !MuseX.VGUIValid() ) then
	
		// not valid
		return;
	
	end
	
	// get from vgui object
	return MuseX.VGUI:GetPlaylist();
	
end


/*------------------------------------
	load song by filename
------------------------------------*/
function MuseX.LoadSong( filename )

	// render file to graph builder
	MuseX.Player:RenderFile( filename );
	
	// goto start
	MuseX.Seek( 0 );
	
	// re-initialize visualization
	MuseX.InitVisualization();
	
	// restore the volume (resets after loading a song)
	if ( MuseX.Muted ) then
	
		// muted
		MuseX.SetVolume( -1 );
	
	else
	
		// regular volume
		MuseX.SetVolume( MuseX.CurrentVolume );
		
	end
	
	// save filename
	MuseX.CurrentSong = filename;

end


/*------------------------------------
	check if song is loaded
------------------------------------*/
function MuseX.SongLoaded( )

	return MuseX.Player:HasFilters();

end


/*------------------------------------
	play a song
------------------------------------*/
function MuseX.Play( )

	// validate the vgui
	if ( !MuseX.VGUIValid() ) then
	
		// not valid
		return;
	
	end
	
	// update the media info
	MuseX.UpdateSongInfo();
	
	// play the loaded song
	MuseX.Player:RunGraph();

end


/*------------------------------------
	update song info for media info
------------------------------------*/
function MuseX.UpdateSongInfo( )

	// make sure a song is loaded
	if ( !MuseX.SongLoaded() ) then
	
		// if there is no song loaded, why update?
		return;
		
	end
	
	// build the media info table
	local info = {
		("Current Song:   %s"):format( MuseX.ExtractFilename( MuseX.CurrentSong ) ),
		"Duration:   $mediatime"
	};
	
	// set the media info
	MuseX.VGUI:SetMediaInfo( info );
	
end

/*------------------------------------
	check if song is playing
------------------------------------*/
function MuseX.IsPlaying( )

	// return bool
	return ( MuseX.Player:IsGraphRunning() );

end



/*------------------------------------
	check if song is paused
------------------------------------*/
function MuseX.IsPaused( )

	// return bool
	return ( MuseX.Player:IsGraphPaused() );

end


/*------------------------------------
	toggle between play & paused
------------------------------------*/
function MuseX.PlayPause( )

	// check if paused
	if ( MuseX.IsPaused() ) then
	
		// play
		MuseX.Player:RunGraph();
	
	else
	
		// pause
		MuseX.Player:PauseGraph();
	
	end

end


/*------------------------------------
	go to previous song in playlist
------------------------------------*/
function MuseX.PreviousSong( force )

	// validate the vgui
	if ( !MuseX.VGUIValid() ) then
	
		// not valid
		return;
	
	end
	
	// get current position
	local pos = MuseX.VGUI:GetPlaylistPosition( MuseX.CurrentSong );
	
	// check if we're on shuffle mode
	if ( MuseX.GetSetting( "shuffle" ) == 1 ) then
	
		// random, random
		return MuseX.PlayRandomSong( pos );
	
	end
	
	// previous position
	pos = pos - 1;
	
	// are we at the top of the playlist
	if ( pos == 0 ) then
	
		// if we're on "repeat list"
		if ( MuseX.GetSetting( "repeat", MXSETTING_NUM ) == 2 ) then
		
			// go to end
			pos = MuseX.VGUI:GetPlaylistCount();
			
		end
		
	end
	
	// get the song at the position
	local song = MuseX.VGUI:GetPlaylistSong( pos );
	
	// make sure we got a song
	if ( song ) then
	
		// get playing state
		local wasplaying = MuseX.IsPlaying() || MuseX.IsPaused();
	
		// load the song
		MuseX.LoadSong( song );
		
		// check if the song was playing
		if ( wasplaying ) then
			
			// play!~
			MuseX.Play();
			
			// success
			return true;
			
		end
	
	end
	
	// no play
	return false;
	
end


/*------------------------------------
	go to next song in playlist
------------------------------------*/
function MuseX.NextSong( force )

	// validate the vgui
	if ( !MuseX.VGUIValid() ) then
	
		// not valid
		return;
	
	end
	
	// get current position
	local pos = MuseX.VGUI:GetPlaylistPosition( MuseX.CurrentSong );
	
	// check if we're on "repeat song" mode
	if ( MuseX.GetSetting( "repeat", MXSETTING_NUM ) == 1 && !force ) then
	
		// stop the song
		MuseX.Stop();
	
		// start over
		MuseX.Play();
		
		// success
		return true;
	
	// check if we're on shuffle mode
	elseif ( MuseX.GetSetting( "shuffle", MXSETTING_NUM ) == 1 ) then
	
		// random, random
		return MuseX.PlayRandomSong( pos );
	
	end
	
	// get total song count
	local max = MuseX.VGUI:GetPlaylistCount();
	
	// increment to next position
	pos = pos + 1;
	
	// are we passed the playlist?
	if ( pos > max ) then
	
		// if we're on "repeat list" or it was forced
		if ( MuseX.GetSetting( "repeat", MXSETTING_NUM ) == 2 || force ) then
		
			// go to start
			pos = 1;
			
		else
		
			// set invalid song number so we don't play anything
			pos = 0;
			
		end
		
	end
	
	// get the song at the position
	local song = MuseX.VGUI:GetPlaylistSong( pos );
	
	// make sure we got a song
	if ( song ) then
	
		// get playing state
		local wasplaying = MuseX.IsPlaying() || MuseX.IsPaused();
	
		// load the song
		MuseX.LoadSong( song );
		
		// check if the song was playing
		if ( wasplaying ) then
			
			// play!~
			MuseX.Play();
			
			// success
			return true;
			
		end
	
	end
	
	// no play
	return false;

end


/*------------------------------------
	play a random song from playlist
------------------------------------*/
function MuseX.PlayRandomSong( notsong )

	// validate the vgui
	if ( !MuseX.VGUIValid() ) then
	
		// not valid
		return;
	
	end
	
	// get playlist size
	local max = MuseX.VGUI:GetPlaylistCount();
	
	// local integer
	local pos;
	
	// we'll try 50 times to get a unique song position
	for i = 1, 50 do
	
		// get a random number
		pos = math.random( 1, max );
	
		// check if its the song
		if ( pos != notsong ) then
		
			// hey, hey! we've got a song
			break;
			
		end
	
	end
	
	// get the song based off the position
	local song = MuseX.VGUI:GetPlaylistSong( pos );
	
	// validate the song
	if ( song ) then
	
		// get playing state
		local wasplaying = MuseX.IsPlaying() || MuseX.IsPaused();
	
		// load the song
		MuseX.LoadSong( song );
		
		// check if the song was playing
		if ( wasplaying ) then
			
			// play!~
			MuseX.Play();
			
			// success
			return true;
			
		end
	
	end
	
	// no play
	return false;
	
end


/*------------------------------------
	stop song
------------------------------------*/
function MuseX.Stop( )

	// validate the vgui
	if ( !MuseX.VGUIValid() ) then
	
		// not valid
		return;
	
	end
	
	// make sure we're not paused
	MuseX.Player:RunGraph();
	
	// stop the loaded song
	MuseX.Player:StopGraph();
	
	// move to front
	MuseX.Seek( 0 );
	
	// clear the media info
	MuseX.VGUI:SetMediaInfo( {} );

end


/*------------------------------------
	seek to a position of the song
------------------------------------*/
function MuseX.Seek( frac )
	
	// make sure the song is loaded
	if ( MuseX.SongLoaded() ) then
	
		// get the total duration
		local duration = MuseX.Player:GetDuration();
		
		// get new position based on fraction
		local pos = duration * frac;
		
		// seek to the new position
		MuseX.Player:SetCurrentPosition( pos );
	
	end

end


/*------------------------------------
	set the position of the seek bar
------------------------------------*/
function MuseX.SetSeekPosition( frac )

	// validate the vgui
	if ( MuseX.VGUIValid() ) then
	
		// pass to vgui
		MuseX.VGUI:SetSeekPosition( frac );
	
	end
	
end


/*------------------------------------
	set the volume level
------------------------------------*/
function MuseX.SetVolume( frac )

	// check for mute
	if ( frac == -1 ) then
	
		// flag as muted
		MuseX.Muted = true;
		
		// set volume level
		MuseX.Player:SetVolume( -10000 );
		
		// we're done
		return;
		
	else
	
		// clamp
		frac = math.Clamp( frac, 0, 1 );
	
	end
	
	// flag as not muted
	MuseX.Muted = nil;

	// calculate new volume value based on fraction
	local vol = ( 1 - frac ) * -4000;
	
	// set volume level
	MuseX.Player:SetVolume( vol );
	
	// update current volume
	MuseX.CurrentVolume = frac;

end


/*------------------------------------
	change the volume level
------------------------------------*/
function MuseX.ChangeVolume( amt, update )

	// validate the vgui
	if ( !MuseX.VGUIValid() ) then
	
		// not valid
		return;
	
	end
	
	// get current volume
	local vol = MuseX.GetVolume();
	
	// alter
	vol = vol + amt;
	
	// set new volume
	MuseX.SetVolume( vol );
	
	// update if needed
	if ( update ) then
	
		// refresh
		MuseX.VGUI:RefreshVolumePosition();
	
	end

end


/*------------------------------------
	get volume level
------------------------------------*/
function MuseX.GetVolume( )

	// get volume level
	local vol = MuseX.Player:GetVolume();
	
	// calculate fraction based on volume level
	local frac = 1 - ( vol / -4000 );
	
	// clamp it
	frac = math.Clamp( frac, 0, 1 );
	
	// store the volume if it is not present
	if ( !MuseX.CurrentVolume ) then
	
		// save it
		MuseX.CurrentVolume = frac;
	
	end
	
	// return fraction
	return frac;

end


/*------------------------------------
	toggle mute
------------------------------------*/
function MuseX.ToggleMute( )

	// calculate volume level based on muted or not
	local vol = ( MuseX.Muted ) && MuseX.CurrentVolume || -1;
	
	// set volume
	MuseX.SetVolume( vol );
	
	// validate the vgui
	if ( MuseX.VGUIValid() ) then
	
		// get the shuffle text
		local text = ( vol == -1 ) && "#musex_mute_on" || "#musex_mute_off";
		
		// show it
		MuseX.VGUI:ShowPlaylistInfo( text );
	
	end

end


/*------------------------------------
	toggle shuffle
------------------------------------*/
function MuseX.ToggleShuffle( )

	// calculate next shuffle value
	local shuffle = ( MuseX.GetSetting( "shuffle", MXSETTING_NUM ) == 0 ) && 1 || 0;
	
	// update setting
	MuseX.SetSetting( "shuffle", shuffle );
	
	// validate the vgui
	if ( MuseX.VGUIValid() ) then
	
		// get the shuffle text
		local text = ( shuffle == 0 ) && "#musex_shuffle_off" || "#musex_shuffle_on";
		
		// show it
		MuseX.VGUI:ShowPlaylistInfo( text );
	
	end

end


/*------------------------------------
	toggle repeat
------------------------------------*/
function MuseX.ToggleRepeat( )

	// caculate next repeat value
	local repeatint = math.Clamp( MuseX.GetSetting( "repeat", MXSETTING_NUM ), 0, 2 ) + 1;
	
	// check for wrap around
	if ( repeatint >= 3 ) then
	
		// start over
		repeatint = 0;
		
	end
	
	// update setting
	MuseX.SetSetting( "repeat", repeatint );
	
	// validate the vgui
	if ( MuseX.VGUIValid() ) then
	
		// table of repeat strings
		local repeattable = { "#musex_repeat_off", "#musex_repeat_song", "#musex_repeat_all" };
		
		// grab the corresponding string
		local text = repeattable[ repeatint + 1 ];
		
		// show it
		MuseX.VGUI:ShowPlaylistInfo( text );
	
	end
	
end
